import { WebSocket } from 'ws';

import proto from '../proto/proto';
import { Bridge, onInnerMgsCallback } from './common/Bridge';
import { ClientConnection, ConnectCloseReason } from './connection/ClientConnection';
import { Server } from './Server';
import { InnerProtocol, L2B_Server } from '../proto/innerMsg';

export enum InnerServer {
    NONE,
    /**大厅 */
    LOBBY,
    /**游戏 */
    GAME,
}

export enum InnerMsgType {
    /** 发送消息 */
    SEND,
    /** 返回消息 */
    BACK,
    /** 推送消息 */
    PUSH,
}

/**
 * 内部消息
 */
export interface InnerMsg {
    /**消息类型 */
    type: InnerMsgType;
    /**发送方 */
    from: InnerServer;
    /**接收方 为空时是推送*/
    to?: InnerServer;
    /**消息数据 */
    body?: any;
    /**调用id */
    seq?: number;
}

/**
 * 启动服务器
 */
export abstract class ClientServer extends Server<ClientConnection> {

    private _bridge: Bridge | null = null;

    private _innerCallMap: Map<number, onInnerMgsCallback> = new Map();
    private _seq: number = 0;

    private _serverId: InnerServer = InnerServer.NONE;

    constructor(serverId: InnerServer) {
        super();
        this._serverId = serverId;
    }

    /**
     * 连接进来的消息
     * @param ws 
     */
    onconnect(ws: WebSocket) {
        const connection = new ClientConnection(ws);
        connection.onMsg = this.onMsg.bind(this);
        connection.onClose = this._onClose.bind(this);
        this._connections.push(connection);
    }

    /**
     * 内部通信服务器
     * @param ip 
     * @param port 
     * @returns 
     */
    startBridge(ip: string, port: number) {
        return new Promise<void>((resolve, reject) => {
            this._bridge = new Bridge(ip, port, () => {
                const body = new L2B_Server()
                body.serverId = this._serverId
                this.sendInnerMsg(InnerProtocol.SERVER, body, 0, () => {
                    resolve()
                })
            }, this._onInnerMsg.bind(this));
        });
    }


    /**
     * 客户端关闭连接(所有的连接关闭都会回调到这里)
     * @param connection 
     * @param reason 
     */
    private _onClose(connection: ClientConnection, reason: ConnectCloseReason) {
        console.log(`${this.constructor.name}---userid:${connection.userId}--connection closed:${reason}`);
        this.removeConnection(connection);
        this.onClose(connection, reason);
    }

    /**
     * 添加到销毁队列
     * @param connection
     */
    deleteConnection(connection: ClientConnection, reason: ConnectCloseReason, delay: number) {
        this.removeConnection(connection);
        connection.setReason(reason);
        setTimeout(() => {
            connection.clear(reason);
        }, delay * 1000)
    }

    /**
     * 获取当前玩家的其他链接
     * @param connection 
     * @returns 
     */
    getUserOnlineConnections(connection: ClientConnection): ClientConnection[] {
        const oldConnections = this._connections.filter(c => c.userId == connection.userId && c != connection)
        return oldConnections;
    }


    /**
     * 回复内部消息
     * @param cmd 
     * @param recvMsg 
     * @param data 
     */
    private _replyInnerMsg(cmd: number, data: InnerMsg, body: any) {
        if (data.type == InnerMsgType.SEND) {
            data.body = body || {}
            data.type = InnerMsgType.BACK
            const from = data.from
            data.from = data.to!
            data.to = from
            this._bridge?.sendData(cmd, data)
        }
    }
    /**
     * 内部消息
     * @param cmd 
     * @param data 
     */
    private _onInnerMsg(cmd: number, data: InnerMsg) {
        if (data.type == InnerMsgType.PUSH) {
            this.onPushInnerMsg(cmd, data)
            return
        }

        if (data.type == InnerMsgType.BACK) {
            const seq = data.seq
            if (seq && this._innerCallMap.has(seq)) {
                this._innerCallMap.get(seq)!(cmd, data)
                this._innerCallMap.delete(seq)
            }
            return
        }

        // 发送的需要返回 
        if (data.type == InnerMsgType.SEND) {
            this.onInnerMsg(cmd, data, (body: any) => {
                this._replyInnerMsg(cmd, data, body)
            })
        }
    }

    /**
     * 内部发送消息
     * @param cmd 
     * @param body 
     */
    sendInnerMsg(cmd: number, body: any = {}, to?: InnerServer, callback?: onInnerMgsCallback) {
        let seq = 0
        if (callback) {
            this._seq = ++this._seq % 1000000000
            if (this._seq == 0) this._seq = 1
            seq = this._seq
            this._innerCallMap.set(this._seq, callback)
        }
        const msg: InnerMsg = { type: InnerMsgType.SEND, from: this._serverId, to, body, seq }
        this._bridge?.sendData(cmd, msg)
    }

    /**
     * 客户端关闭
     * @param connection 
     * @param reason 
     */
    abstract onClose(connection: ClientConnection, reason: ConnectCloseReason): void

    /**
     * 客户端消息
     * @param connection 
     * @param cmd 
     * @param reqWrap 
     * @param data 
     */
    abstract onMsg(connection: ClientConnection, cmd: number, reqWrap: proto.ReqWrap, body: any): void;

    /**
     * 收到内部消息
     * 回复消息使用 reply()
     * @param cmd 
     * @param data 
     * @param reply
     */
    abstract onInnerMsg(cmd: number, data: InnerMsg, reply: (body?: any) => void): void

    /**
     * 接收推送消息
     * @param cmd 
     * @param data 
     */
    abstract onPushInnerMsg(cmd: number, data: InnerMsg): void


}
